﻿using System;
using System.IO;

namespace ooWii.IO
{
	public class GCDiscHeader
	{
		#region GCDiscHeader

		GCRootDirectoryInfo rootDirectory;

		public GCDiscHeader( Stream partition )
		{
			byte[] discHeaderBuffer = new byte[ 0x440 ];

			partition.Position = 0;
			if( partition.Read( discHeaderBuffer, 0, discHeaderBuffer.Length ) != discHeaderBuffer.Length )
			{
				throw new GCException( "Cannot read disc header buffer" );
			}

			rootDirectory = new GCRootDirectoryInfo();

			/// app loader
			long aplOffset = 0x2440;
			byte[] aplHeaderBuffer = new byte[ 0x20 ];

			partition.Position = aplOffset;
			if( partition.Read( aplHeaderBuffer, 0, aplHeaderBuffer.Length ) != aplHeaderBuffer.Length )
			{
				throw new GCException( "Cannot read apl header" );
			}

			UInt32 appLoaderSize = WiiBuffer.GetUInt32( aplHeaderBuffer, 0x14 );
			UInt32 appTrailerSize = WiiBuffer.GetUInt32( aplHeaderBuffer, 0x18 );
			rootDirectory.AddFile( new GCFileInfo( partition, rootDirectory, "###apl###", aplOffset, (UInt32)( aplHeaderBuffer.Length + appLoaderSize + appTrailerSize ) ) );

			/// main executable
			long dolOffset = WiiBuffer.GetUInt34( discHeaderBuffer, 0x420 );
			byte[] dolHeader = new byte[ 0x100 ];

			partition.Position = dolOffset;
			if( partition.Read( dolHeader, 0, dolHeader.Length ) != dolHeader.Length )
			{
				throw new GCException( "Cannot read dol header" );
			}

			UInt32 dolSize = (UInt32)dolHeader.Length;

			for( int section = 0; section < 18; section++ )
			{
				UInt32 sectionStart = WiiBuffer.GetUInt32( dolHeader, section * 4 );
				UInt32 sectionLength = WiiBuffer.GetUInt32( dolHeader, 0x90 + ( section * 4 ) );
				if( sectionLength > 0 )
				{
					UInt32 sectionEnd = sectionStart + sectionLength;
					if( dolSize < sectionEnd )
					{
						dolSize = sectionEnd;
					}
				}
			}

			rootDirectory.AddFile( new GCFileInfo( partition, rootDirectory, "###dol###", dolOffset, dolSize ) );

			/// files
			long fstOffset = WiiBuffer.GetUInt34( discHeaderBuffer, 0x424 );
			UInt32 fstSize = WiiBuffer.GetUInt32( discHeaderBuffer, 0x428 );

			rootDirectory.Load( partition, fstOffset, fstSize );
		}

		public GCDirectoryInfo RootDirectory
		{
			get
			{
				return rootDirectory;
			}
		}

		public Stream OpenRead( string path )
		{
			string[] parts = path.Split( '/' );

			GCDirectoryInfo currentDirectory = rootDirectory;

			for( int i = 0; i < parts.Length - 1; i++ )
			{
				string directoryName = parts[ i ];
				if( directoryName.IndexOfAny( new char[] { '?', '*' } ) >= 0 )
				{
					throw new Exception();
				}

				GCDirectoryInfo[] directories = currentDirectory.GetDirectories( directoryName );
				if( directories.Length == 0 )
				{
					throw new Exception();
				}

				currentDirectory = directories[ 0 ];
			}

			string fileName = parts[ parts.Length - 1 ];
			GCFileInfo[] files = currentDirectory.GetFiles( fileName );
			if( files.Length == 0 )
			{
				throw new Exception();
			}

			return files[ 0 ].OpenRead();
		}

		#endregion
	}
}
